home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 26
/
Cream of the Crop 26.iso
/
os2
/
which213.zip
/
src-org
/
which.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-28
|
21KB
|
574 lines
/*---------------------------------------------------------------------------
which.c
This program by default finds the path of an executable command under
either OS/2 or MS-DOS. It can also be used to find data files in the
DPATH or DLLs in the LIBPATH (at least under OS/2). It does not have
a whole lot of error-checking, but the array sizes are reasonably gen-
erous.
To do: add support for ksh, bash, etc.; allow wildcard commands?
Copyright (c) 1993 by Greg Roelofs. 4OS2/4DOS spawnl() code by Michael
D. Lawler. DosQuerySysInfo() code by Kenneth Porter. You may use this
code for any purpose whatsoever, as long as you don't sell it and don't
claim to have written it. Not that you'd want to.
---------------------------------------------------------------------------*/
#define VERSION "v2.1 of 28 July 1993"
/* #define DEBUG */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <process.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
/* not used:
#if defined(__OS2__) && !defined(OS2)
# define OS2
#endif
*/
#if defined(__IBMC__)
# define S_IFMT 0xF000 /* or (S_IFREG | S_IFDIR | S_IFCHR) */
#endif
#ifndef S_ISDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#if defined(__IBMC__) || defined(__EMX__) || defined(__WATCOMC__)
# ifndef __32BIT__
# define __32BIT__
# endif
# define INCL_DOSMISC /* for IBM Toolkit headers */
# define INCL_DOSERRORS /* for IBM Toolkit headers */
# define INCL_NOPMAPI /* for IBM Toolkit headers */
# include <os2.h>
#endif
#ifdef DEBUG
# define Trace(x) fprintf x
#else
# define Trace(x)
#endif
int get_libpath(char **path); /* our sole prototype... */
struct stat statbuf;
/* extensions must be in order of operating-system precedence! */
static char *ext_command[] = { /* COMMAND extensions */
".com",
".exe",
".bat"
};
static char *ext_cmd[] = { /* CMD extensions */
".com",
".exe",
".cmd",
".bat"
};
static char *ext_4dos[] = { /* 4DOS extensions */
".com",
".exe",
".btm",
".bat"
};
static char *ext_4os2[] = { /* 4OS2 extensions */
".com",
".exe",
".btm",
".cmd",
".bat"
};
static char *ext_libpath[] = { /* LIBPATH extension(s) */
".dll"
};
static char *ext_dpath[] = { /* DPATH extension(s) */
".boo",
".dat",
".inf",
".ini", /* does this belong here? */
".hlp",
".msg",
".ndx"
};
/* OS/2 internal commands: some of these are rarely used outside of
* command files, but all of them can be called from the command line.
* (I don't know if some of the OS/2-only commands might be in later
* versions of MS-DOS. One could use spawnl() and check the errors,
* but that would be slow.)
*/
static char *int_command[] = {
"call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
"prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
"time", "type", "ver", "verify", "vol"
};
static char *int_cmd[] = {
/* note that extproc cannot be called from command line in 4OS2 */
"chcp", "detach", "dpath", "endlocal", "extproc", "keys", "move",
"setlocal", "start",
/* all the rest are identical to int_command[] */
"call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
"prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
"time", "type", "ver", "verify", "vol"
};
typedef struct shellinfo {
char *name;
char **extension;
int num_ext;
char **internal;
int num_int;
} SHELLINFO;
/* these guys must match the order of shells[] */
#define _COMMAND 0
#define _CMD 1
#define _4DOS 2
#define _4OS2 3
#define _4OS2_16 4
#define _4OS2_32 5
SHELLINFO shells[] = {
{"COMMAND.COM", ext_command, sizeof(ext_command)/sizeof(char *),
int_command, sizeof(int_command)/sizeof(char *) },
{"CMD.EXE", ext_cmd, sizeof(ext_cmd)/sizeof(char *),
int_cmd, sizeof(int_cmd)/sizeof(char *) },
{"4DOS.COM", ext_4dos, sizeof(ext_4dos)/sizeof(char *),
(char **)NULL, 0 },
{"4OS2.EXE", ext_4os2, sizeof(ext_4os2)/sizeof(char *),
(char **)NULL, 0 },
{"4OS2-16.EXE", ext_4os2, sizeof(ext_4os2)/sizeof(char *),
(char **)NULL, 0 },
{"4OS2-32.EXE", ext_4os2, sizeof(ext_4os2)/sizeof(char *),
(char **)NULL, 0 }
};
#define REGPATH 0 /* for pathtype */
#define LIBPATH 1
#define DPATH 2
char *prognam;
/****************/
/* Main program */
/****************/
int main(int argc, char *argv[])
{
static char tempname[1024];
char *p, *q, *comspec, *path, *pathvar, *dir[1000];
int i, j, k, c, error=0, startdir=0;
int sh, all=FALSE, pathtype=REGPATH, regpath, fourshell=FALSE;
int numdirs=0, numshells=sizeof(shells)/sizeof(SHELLINFO);
/*---------------------------------------------------------------------------
Parse the command line...
---------------------------------------------------------------------------*/
prognam = argv[0];
p = prognam - 1;
while (*++p)
*p = tolower(*p); /* assumes "smart" tolower() */
if (argc <= 1)
--argc;
else
while (--argc > 0 && (*++argv)[0] == '-')
while ((c = *++(argv[0])) != '\0')
switch (c) {
case 'a': /* list all matches */
all = TRUE;
break;
case 'l': /* search for DLLs */
pathtype = LIBPATH;
break;
case 'd': /* search for data files */
pathtype = DPATH;
break;
default:
++error;
break;
} /* end switch, while, while, if */
/*---------------------------------------------------------------------------
Print usage if any errors or if no arguments.
---------------------------------------------------------------------------*/
if (error || (argc <= 0)) {
#ifdef TRADITIONAL
fprintf(stderr, "%s: too few arguments\n", prognam);
#else
fprintf(stderr, "\n\
which (%s) for OS/2 %s, from Newtware\n\n\
usage: %s [ -a ] [ -l | -d ] cmd [ cmd ... ]\n\
(default behavior is to find location of program executed as \"cmd\")\n\
-a list all matches, not just first\n\
-l search directories in LIBPATH for DLLs%s\n\
-d search directories in DPATH for data files%s\n", VERSION,
#ifdef __32BIT__
"2.x", prognam, "", "");
#else
"and MS-DOS", prognam, " (OS/2)", " (OS/2)");
#endif /* ?__32BIT__ */
#endif /* ?TRADITIONAL */
exit(1);
}
/*---------------------------------------------------------------------------
Try to figure out what shell we're in, based on COMSPEC.
---------------------------------------------------------------------------*/
if ((comspec = getenv("COMSPEC")) == (char *)NULL || *comspec == '\0') {
Trace((stderr, "COMSPEC is empty...assuming COMSPEC = cmd.exe\n"));
sh = _CMD;
} else {
Trace((stderr, "COMSPEC = %s\n", comspec));
p = strrchr(comspec, '\\');
if (p == NULL)
p = comspec;
else
++p;
for (i = 0; i < numshells; ++i) {
if (stricmp(p, shells[i].name) == 0) {
sh = i;
break;
}
}
if (i == numshells) {
sh = _CMD;
fprintf(stderr,
"%s: unknown command shell \"%s\"\n%s: assuming %s\n",
prognam, comspec, prognam, shells[sh].name);
}
if (sh >= 2 && sh <= 5) /* 4DOS, 4OS2, 4OS2-16, 4OS2-32 */
fourshell = TRUE;
}
Trace((stderr, "shell is %s\n\n", shells[sh].name));
/*---------------------------------------------------------------------------
Get the PATH, DPATH or LIBPATH, depending on the user's wishes.
---------------------------------------------------------------------------*/
/* for regular path, current directory is always implied; not for others */
switch (pathtype) {
case REGPATH:
pathvar = "PATH";
regpath = TRUE;
dir[numdirs++] = ".";
path = getenv(pathvar);
break;
case DPATH:
pathvar = "DPATH";
regpath = FALSE;
shells[sh].extension = ext_dpath;
shells[sh].num_ext = sizeof(ext_dpath)/sizeof(char *);
path = getenv(pathvar);
break;
case LIBPATH:
pathvar = "LIBPATH";
regpath = FALSE;
shells[sh].extension = ext_libpath;
shells[sh].num_ext = sizeof(ext_libpath)/sizeof(char *);
get_libpath(&path);
break;
}
Trace((stderr, "COMSPEC now = %s\n", comspec));
/*---------------------------------------------------------------------------
Terminate the path elements and store pointers to each one.
---------------------------------------------------------------------------*/
if (path == (char *)NULL || *path == '\0') {
Trace((stderr, "\n%s is empty\n\n", pathvar));
if (!regpath) {
fprintf(stderr, "%s: %s is empty\n", prognam, pathvar);
exit(2);
}
} else {
Trace((stderr, "\n%s = %s\n\n", pathvar, path));
if (*path != ';')
dir[numdirs++] = path;
p = path - 1;
while (*++p)
if (*p == ';') {
*p = '\0';
if ((p[1] != '\0') && (p[1] != ';'))
dir[numdirs++] = p + 1;
} else
*p = tolower(*p); /* should probably make this an option... */
}
/*---------------------------------------------------------------------------
If we're doing a normal PATH search under 4OS2 or 4DOS, check the path
for a "." entry; if find one, ignore the extra "." entry which was previ-
ously inserted at the beginning of the dir[] array. (Entries of the form
"d:." don't count, and CMD and COMMAND always insert the "." first.)
---------------------------------------------------------------------------*/
if (fourshell && pathtype == REGPATH) {
for (i = 1; i < numdirs; ++i)
if (dir[i][0] == '.' && dir[i][1] == '\0') { /* "." */
startdir = 1;
break;
}
}
/*---------------------------------------------------------------------------
For each command or file given as an argument, check all of the direc-
tories in the appropriate path. For commands, first see if it's an in-
ternal command or (in the case of 4DOS/4OS2) an alias; if not, search
the path for it. For each directory in the path, see if the OS will con-
sider it a command as is (it has a dot in its name), and if so whether
it exists; then try appending each extension (in order of precedence)
and again check for existence. For data files or DLLs, just check direc-
tories in the path for the filename or the filename with an appropriate
extension (".dll", ".inf", etc.) appended.
---------------------------------------------------------------------------*/
for (j = 0; j < argc; ++j) {
int hasdot, found=0;
/* don't bother with internal commands if argument has a dot */
hasdot = (strchr(argv[j], (int)'.') != (char *)NULL);
if (regpath && !hasdot) {
Trace((stderr, "checking %s internals\n", shells[sh].name));
/* 4DOS/4OS2 have tests for internals/aliases, so check smartly */
if (fourshell) {
char *tempenv[2] = {tempname, NULL};
int rc;
#ifdef COMMANDSEP_WORKS /* This form always fails (get correct response only
* if first condition is true, i.e., both alias and
* internal command). It appears to be another bug
* in 4OS2 (rc always = 0). The line is too long for
* 4DOS in any case (257 bytes for 5-char alias name).
*/
sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal %s "
"then %%+ echos %s: aliased to \"%%@alias[%s]\" %%+ exit 43 %%+"
" elseiff isalias %s then %%+ echos %s: aliased to \"%%@alias"
"[%s]\" %%+ exit 42 %%+ elseiff isinternal %s then %%+ exit 41 "
"%%+ else %%+ exit 40 %%+ endiff", argv[j], argv[j],
argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
strlen(tempname), tempname));
rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
tempenv);
Trace((stderr, "4WHICH return code = %d\n", rc));
#else /* !COMMANDSEP_WORKS */
/* quotes around alias necessary due to 4DOS/4OS2 bug */
if (sh != _4DOS) { /* 4OS2: can do in one long command */
sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
"%s then & echos %s: aliased to \"%%@alias[%s]\" & exit "
"43 & elseiff isalias %s then & echos %s: aliased to "
"\"%%@alias[%s]\" & exit 42 & elseiff isinternal %s then "
"& exit 41 & else & exit 40 & endiff", argv[j], argv[j],
argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
strlen(tempname), tempname));
/* "/c", "4which" arguments must be separate for emx+gcc */
rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%",
NULL, tempenv);
Trace((stderr, "4WHICH return code = %d\n", rc));
if (rc == 42 || rc == 43) /* alias (and internal if 43) */
++found;
} else {
sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
"%s then ^ exit 43 ^ elseiff isalias %s then ^ exit 42 ^ "
"elseiff isinternal %s then ^ exit 41 ^ else ^ exit 40 ^ "
"endiff", argv[j], argv[j], argv[j], argv[j]);
Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
strlen(tempname), tempname));
rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
tempenv);
Trace((stderr, "4WHICH return code = %d\n", rc));
if (rc == 42 || rc == 43) { /* alias */
++found;
sprintf(tempname, "echos %s: aliased to \"%%@alias"
"[%s]\"", argv[j], argv[j]);
Trace((stderr, "2nd cmd length = %d bytes\n\n%s\n\n",
strlen(tempname), tempname));
spawnl(P_WAIT, comspec, comspec, "/c", tempname, NULL);
}
}
#endif /* ?COMMANDSEP_WORKS */
if (rc == 41 || (rc == 43 && all)) { /* internal */
++found;
sprintf(tempname, "%s internal command", shells[sh].name);
if (rc == 41)
printf("%s: %s", argv[j], tempname);
else
printf(" (also %s", tempname);
}
} else {
/* quit as soon as found: only one internal match allowed */
for (i = 0; (i < shells[sh].num_int) && !found; ++i) {
Trace((stderr, "checking %s\n", shells[sh].internal[i]));
if (stricmp(argv[j], shells[sh].internal[i]) == 0) {
++found;
printf("%s: %s internal command", argv[j],
shells[sh].name);
break;
}
}
}
}
for (i = startdir; (i < numdirs) && (!found || all); ++i) {
p = tempname;
q = dir[i];
while ((*p++ = *q++) != '\0'); /* p now points to char *after* 0 */
if (p[-2] == '\\') /* could be root dir (e.g., c:\) */
--p;
else /* replace null with dir sep. */
p[-1] = '\\';
q = argv[j];
while ((*p++ = *q++) != '\0'); /* copy program name */
--p; /* point at null */
if (!regpath || hasdot) {
Trace((stderr, "checking %s\n", tempname));
if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
if (!found || all) {
if (found == 1)
printf(" (also %s", tempname);
else
printf("%s%s", found? ", " : "", tempname);
}
++found;
if (!all)
break; /* quit right now unless finding all */
}
}
for (k = 0; (k < shells[sh].num_ext) && (!found || all); ++k) {
strcpy(p, shells[sh].extension[k]);
Trace((stderr, "checking %s\n", tempname));
if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
if (!found || all) {
if (found == 1)
printf(" (also %s", tempname);
else
printf("%s%s", found? ", " : "", tempname);
}
++found;
if (!all)
break; /* quit right now unless finding all */
}
}
} /* end i-loop */
if (!found) {
printf("no %s in", argv[j]);
for (i = 0; i < numdirs; ++i)
printf(" %s", dir[i]);
}
printf("%s\n", (found > 1)? ")" : "");
}
return 0;
}
/**************************/
/* Function get_libpath() */
/**************************/
int get_libpath(char **path)
{
char *line, *p, config_sys[]="c:\\config.sys";
int foundcfg=FALSE;
#ifdef __32BIT__
int drive;
APIRET rc;
#endif
FILE *cfg;
if ((line = (char *)malloc(4096)) == NULL) {
fprintf(stderr, "%s: not enough memory\n", prognam);
exit(8);
}
#ifdef __32BIT__
rc = DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &drive, sizeof(drive));
if (rc == NO_ERROR) {
config_sys[0] = drive - 1 + 'a';
if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
(cfg = fopen(config_sys, "r")) != NULL)
foundcfg = TRUE;
} else {
fprintf(stderr, "%s: internal error (%d)\n", prognam, rc);
#endif
if (!stat(config_sys+2, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
(cfg = fopen(config_sys+2, "r")) != NULL)
foundcfg = TRUE;
else if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
(cfg = fopen(config_sys, "r")) != NULL)
foundcfg = TRUE;
#ifdef __32BIT__
}
#endif
if (!foundcfg) {
fprintf(stderr, "%s: can't find config.sys\n", prognam);
*path = (char *)NULL;
return -1;
}
/* assume cfg is open file pointer to config.sys */
while (fgets(line, 4096, cfg)) {
if (strnicmp(line, "LIBPATH", 7)) /* assumes no leading spaces: ? */
continue;
/* get rid of trailing newline, if any */
if ((p=strrchr(line, '\n')) != NULL)
*p = '\0';
Trace((stderr, "found LIBPATH line:\n%s\n", line));
p = strchr(line+7, '=');
if (p == NULL) {
*path = (char *)NULL;
return -1;
}
while (*++p == ' ');
*path = p;
return 0;
}
/* LIBPATH not found in config.sys */
*path = (char *)NULL;
return -1;
}